package com.java.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

public class SimpleIO {

	/**
	 * 无缓存75.952s
	 */
	@Test
	public void fileTest() {

		File src = new File("D:/myBase_V7b20.zip");// 源文件
		File des = new File("D:/w1.zip");// 目标文件
		InputStream in = null;
		OutputStream out = null;
		try {
			in = new FileInputStream(src);
			out = new FileOutputStream(des);
			int len = 0;// 定义一个int类型的变量b,记住每次读取一个字节
			while ((len = in.read()) != -1) {// 判断是否去到文件末尾
				out.write(len);// 向文件里写一个字节
			}

		} catch (Exception e) {
			try {
				in.close();
				out.close();
			} catch (IOException e1) {
				// TODO: handle exception
			}
			// TODO: handle exception

		}

	}

	/**
	 * 有缓存0.107s
	 */
	@Test
	public void bufferFile() {
		File src = new File("D:/aa.iso");// 源文件
		File des = new File("D:/b1.iso");// 目标文件
		InputStream in = null;
		OutputStream out = null;
		try {
			in = new FileInputStream(src);
			out = new FileOutputStream(des);
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = in.read(buffer)) != -1) {
				out.write(buffer, 0, len);
			}
		} catch (Exception e) {
			// TODO: handle exception
			try {
				in.close();
				out.close();
			} catch (Exception e1) {
				// TODO: handle exception
			}
		}
	}

	/**
	 * 拆分文件
	 * 
	 * @param fileName
	 *            待拆分的完整文件名
	 * @param byteSize
	 *            按多少字节大小拆分
	 * @return 拆分后的文件名列表
	 * @throws IOException
	 */
	public List<String> splitBySize(String fileName, int byteSize) throws IOException {
		List<String> parts = new ArrayList<String>();
		File file = new File(fileName);
		int count = (int) Math.ceil(file.length() / (double) byteSize);
		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(count, count * 3, 1, TimeUnit.SECONDS,
				new ArrayBlockingQueue<Runnable>(count * 2));
		for (int i = 0; i < count; i++) {
			String partFileName = "D:/" + file.getName() + "." + (i + 1) + "" + ".part";
			threadPool.execute(new SplitRunnable(byteSize, i * byteSize, partFileName, file));

			parts.add(partFileName);
		}
		threadPool.shutdown();
		return parts;
	}

	private class SplitRunnable implements Runnable {
		int byteSize;
		String partFileName;
		File originFile;
		int startPos;

		public SplitRunnable(int byteSize, int startPos, String partFileName, File originFile) {
			this.startPos = startPos;
			this.byteSize = byteSize;
			this.partFileName = partFileName;
			this.originFile = originFile;
		}

		public void run() {
			RandomAccessFile rFile;
			OutputStream os;
			try {
				rFile = new RandomAccessFile(originFile, "r");
				int i = 0;
				//防止分割后文件还是过大加载内存中导致内存溢出
				byte[] b = new byte[1024];
				rFile.seek(startPos);// 移动指针到每“段”开头
				os = new FileOutputStream(partFileName);
				int s = 0;
				while ((s = rFile.read(b)) != -1) {
					os.write(b, 0, s);
					i = i + b.length;
					if (i >= byteSize)
						break;
				}
				os.flush();
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		//测试拆分文件
//		List<String> names = null;
//		try {
//			names = new SimpleIO().splitBySize("D:/znafcode.zip", 1048576);
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		for (String string : names) {
//			System.out.println(string);
//		}
		//测试合并文件
		try {
			new SimpleIO().mergePartFiles("D:/", ".part", 1048576, "D:/asdfsdsd.zip");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 合并文件
	 * 
	 * @param dirPath
	 *            拆分文件所在目录名
	 * @param partFileSuffix
	 *            拆分文件后缀名
	 * @param partFileSize
	 *            拆分文件的字节数大小
	 * @param mergeFileName
	 *            合并后的文件名
	 * @throws IOException
	 */
	public void mergePartFiles(String dirPath, String partFileSuffix, int partFileSize, String mergeFileName)
			throws IOException {
		ArrayList<File> partFiles = new ArrayList<>();
		for (int i = 1; i <= 52; i++) {
			partFiles.add(new File("D:/znafcode.zip." + i + ".part"));
		}

		RandomAccessFile randomAccessFile = new RandomAccessFile(mergeFileName, "rw");
		randomAccessFile
				.setLength(partFileSize * (partFiles.size() - 1) + partFiles.get(partFiles.size() - 1).length());
		randomAccessFile.close();

		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(partFiles.size(), partFiles.size() * 3, 1,
				TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(partFiles.size() * 2));

		for (int i = 0; i < partFiles.size(); i++) {
			threadPool.execute(new MergeRunnable(i * partFileSize, mergeFileName, partFiles.get(i)));
		}
		threadPool.shutdown();

	}

	private class MergeRunnable implements Runnable {
		long startPos;
		String mergeFileName;
		File partFile;

		public MergeRunnable(long startPos, String mergeFileName, File partFile) {
			this.startPos = startPos;
			this.mergeFileName = mergeFileName;
			this.partFile = partFile;
		}

		public void run() {
			RandomAccessFile rFile;
			try {
				rFile = new RandomAccessFile(mergeFileName, "rw");
				rFile.seek(startPos);
				FileInputStream fs = new FileInputStream(partFile);
				byte[] b = new byte[fs.available()];
				fs.read(b);
				fs.close();
				rFile.write(b);
				rFile.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
